{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个教程中，你将会学到怎么用Folium制作可交互地图可视化  \n",
    "\n",
    "提供的基础数据是：\n",
    "<div class=\"alert alert-info\"><h2>提供的基础数据是：</h2><p>   \n",
    "    GIS文件：<br>  \n",
    "    1.深圳行政区划<br>  \n",
    "    2.深圳栅格<br>  \n",
    "    <br>  \n",
    "    数据：<br>  \n",
    "    1.出租车GPS集计栅格OD<br>  \n",
    "\n",
    " </p></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:26:02.255808Z",
     "start_time": "2020-01-19T08:26:01.024694Z"
    }
   },
   "outputs": [],
   "source": [
    "#导入必要的包\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "#绘制图用的包\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "#geopandas包\n",
    "import geopandas\n",
    "\n",
    "#shapely包\n",
    "from shapely.geometry import Point,Polygon,shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 读取基础数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## GIS数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:27:21.873831Z",
     "start_time": "2020-01-19T08:27:21.724194Z"
    }
   },
   "outputs": [],
   "source": [
    "#行政区划数据\n",
    "#读取shapefile文件\n",
    "shp = r'shapefile\\sz.shp'\n",
    "xzqh = geopandas.GeoDataFrame.from_file(shp,encoding = 'utf-8')\n",
    "\n",
    "#绘制看看长什么样\n",
    "xzqh.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:27:47.439438Z",
     "start_time": "2020-01-19T08:27:45.408870Z"
    }
   },
   "outputs": [],
   "source": [
    "#栅格数据\n",
    "#读取shapefile文件\n",
    "shp =  r'shapefile\\grid\\grid.shp'\n",
    "grid = geopandas.GeoDataFrame.from_file(shp,encoding = 'gbk')\n",
    "\n",
    "#绘制看看长什么样\n",
    "grid.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 栅格OD数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个数据是前面教程2中，用公式计算出来的OD"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:29:58.665266Z",
     "start_time": "2020-01-19T08:29:58.582447Z"
    }
   },
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "OD = pd.read_csv(r'data-sample\\taxi_od_grid.csv')\n",
    "OD.head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Folium可视化工具教程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  folium是js上著名的地理信息可视化库leaflet.js为Python提供的接口，通过它，我们可以通过在Python端编写代码操纵数据，来调用leaflet的相关功能，基于内建的osm或自行获取的osm资源和地图原件进行地理信息内容的可视化，以及制作优美的可交互地图。其语法格式类似ggplot2，是通过不断添加图层元素来定义一个Map对象，最后以几种方式将Map对象展现出来。\n",
    "    \n",
    "  而在Map对象的生成形式上，可以在定义所有的图层内容之后，将其保存为html文件在浏览器中独立显示，也可以基于jupyter notebook在一个ipynb文件内部嵌入对应的交互地图，本文即采用后者对应的方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-09-11T11:59:34.836170Z",
     "start_time": "2019-09-11T11:59:34.481491Z"
    }
   },
   "outputs": [],
   "source": [
    "import folium"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "好的结束  \n",
    "\n",
    "不，没有结束，pip install folium  \n",
    "我安装好了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:30:10.739032Z",
     "start_time": "2020-01-19T08:30:09.771324Z"
    }
   },
   "outputs": [],
   "source": [
    "import folium"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 栅格绘制"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先，创建地图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#快看一下创建地图有哪些参数\n",
    "?folium.Map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:34:46.607329Z",
     "start_time": "2020-01-19T08:34:46.449715Z"
    }
   },
   "outputs": [],
   "source": [
    "###############################你需要在下面写代码##################################\n",
    "# 在深圳行政区划的中心xzqh创建地图\n",
    "# 调整缩放水平为10\n",
    "\n",
    "###################################################################################\n",
    "\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "###############################     答  案      ##################################\n",
    "\n",
    "'''\n",
    "其主要参数如下：\n",
    "location：tuple或list类型输入，用于控制初始地图中心点的坐标，格式为(纬度，经度)或[纬度，经度]，默认为None\n",
    "width：int型或str型，int型时，传入的是地图宽度的像素值；str型时，传入的是地图宽度的百分比，形式为'xx%'。默认为'100%'\n",
    "height：控制地图的高度，格式同width\n",
    "tiles：str型，用于控制绘图调用的地图样式，默认为'OpenStreetMap'，也有一些其他的内建地图样式，如'Stamen  Terrain'、'Stamen Toner'、'Mapbox Bright'、'Mapbox Control Room'等；也可以传入'None'来绘制一个没有风格的朴素地图，或传入一个URL来使用其它的自选osm\n",
    "max_zoom：int型，控制地图可以放大程度的上限，默认为18\n",
    "attr：str型，当在tiles中使用自选URL内的osm时使用，用于给自选osm命名\n",
    "control_scale：bool型，控制是否在地图上添加比例尺，默认为False即不添加\n",
    "no_touch：bool型，控制地图是否禁止接受来自设备的触控事件譬如拖拽等，默认为False，即不禁止\n",
    "'''\n",
    "m = folium.Map(location=[xzqh.unary_union.centroid.y,xzqh.unary_union.centroid.x],\n",
    "              zoom_start=10)\n",
    "###################################################################################\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，在地图上添加东西"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-09-11T12:27:23.899940Z",
     "start_time": "2019-09-11T12:27:23.869445Z"
    }
   },
   "source": [
    "folium的图形绘制支撑geojson格式，这种格式可以理解为一种以文本格式存储的地理空间数据，类似shp，只不过是以另一个标准保存  \n",
    "从geopandas很容易可以得到geojson格式，对geodataframe用.to_json()方法就行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:34:49.369920Z",
     "start_time": "2020-01-19T08:34:49.197384Z"
    },
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "folium.GeoJson(\n",
    "    xzqh.to_json(),\n",
    "    name='深圳行政区划'\n",
    ").add_to(m)\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:32:48.175457Z",
     "start_time": "2020-01-19T08:32:48.115620Z"
    }
   },
   "outputs": [],
   "source": [
    "###############################你需要在下面写代码##################################\n",
    "#我们对OD数据的['SLONCOL','SLATCOL']字段集计，也就是对O点集计\n",
    "#然后再连接到grid上，命名为gridtoplot\n",
    "\n",
    "\n",
    "###################################################################################\n",
    "\n",
    "gridtoplot.head(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "###############################     答  案      ##################################\n",
    "#我们对OD数据的['SLONCOL','SLATCOL']字段集计，也就是对O点集计\n",
    "#然后再连接到grid上，命名为gridtoplot\n",
    "O = OD.groupby(['SLONCOL','SLATCOL'])['VehicleNum'].sum().reset_index()\n",
    "O.columns = ['LONCOL','LATCOL','count']\n",
    "gridtoplot =pd.merge(grid,O,on = ['LONCOL','LATCOL'])\n",
    "\n",
    "###################################################################################"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:34:38.571816Z",
     "start_time": "2020-01-19T08:34:38.565795Z"
    }
   },
   "outputs": [],
   "source": [
    "#这里我们只显示count为10个以上的格子，否则矢量图象太多显示不出来\n",
    "gridtoplot = gridtoplot[gridtoplot['count']>10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:34:55.866577Z",
     "start_time": "2020-01-19T08:34:55.332005Z"
    }
   },
   "outputs": [],
   "source": [
    "#好的我已经写好了这个函数\n",
    "def foliumgridplot(m,grid,column,fill_color='YlOrRd',fill_opacity = 0.7,line_opacity =0.1,legend_name = '',name = ''):\n",
    "    folium.Choropleth(\n",
    "        name = name,\n",
    "        geo_data=grid.reset_index().to_json(),\n",
    "        data=grid[column].reset_index(),\n",
    "        columns=['index', column],\n",
    "        key_on='feature.properties.index',\n",
    "        fill_color=fill_color,\n",
    "        fill_opacity=fill_opacity,\n",
    "        line_opacity=line_opacity,\n",
    "        legend_name=legend_name\n",
    "    ).add_to(m)\n",
    "    return m\n",
    "\n",
    "#直接这样就能绘制\n",
    "foliumgridplot(m,gridtoplot,'count',name = '出租车O点分布')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:35:44.287731Z",
     "start_time": "2020-01-19T08:35:43.917725Z"
    }
   },
   "outputs": [],
   "source": [
    "#给右边添加一个图层管理器\n",
    "folium.LayerControl().add_to(m)\n",
    "\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:35:48.630426Z",
     "start_time": "2020-01-19T08:35:48.259422Z"
    }
   },
   "outputs": [],
   "source": [
    "#给罗湖火车站打个标签\n",
    "folium.Marker([22.530967,114.113088],\n",
    "              popup='罗湖火车站',\n",
    "              icon=folium.Icon(icon='fa-train',prefix = 'fa')).add_to(m)\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[这个网页上有各种图标](https://faicons.com/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 热力图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:35:56.970087Z",
     "start_time": "2020-01-19T08:35:56.960156Z"
    }
   },
   "outputs": [],
   "source": [
    "gridtoplot[['HBLAT','HBLON','count']].head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注：先纬度后经度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-09-12T01:29:45.626306Z",
     "start_time": "2019-09-12T01:29:45.246727Z"
    }
   },
   "outputs": [],
   "source": [
    "?HeatMap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-19T08:36:02.052593Z",
     "start_time": "2020-01-19T08:36:01.768359Z"
    },
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "from folium.plugins import HeatMap\n",
    "###############################你需要在下面写代码##################################\n",
    "#在图上加入热力图\n",
    "\n",
    "\n",
    "###################################################################################\n",
    "\n",
    "\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from folium.plugins import HeatMap\n",
    "###############################     答  案      ##################################\n",
    "#在图上加入热力图\n",
    "m = folium.Map(location=[xzqh.unary_union.centroid.y,xzqh.unary_union.centroid.x],\n",
    "              zoom_start=11)\n",
    "\n",
    "HeatMap(gridtoplot[['HBLAT','HBLON','count']].values,max_val=1,radius=13,blur = 25).add_to(m)\n",
    "\n",
    "\n",
    "###################################################################################\n",
    "\n",
    "\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Folium的优点很明显，出GIS图需要代码很少，与geopandas的兼容性很好  \n",
    "不过用Folium可视化出图，亲测一万个矢量图形就已经很卡了  \n",
    "所以大规模（矢量元素达到万级别）批量出（一次性出多张图）专题图还是用geopandas+matplotlib吧  \n",
    "  \n",
    "更多案例请参考：[宇宙最大同性交友网站](https://github.com/python-visualization/folium)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 作业"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "尝试用Folium绘制OD图"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "409.091px",
    "left": "141px",
    "top": "214.322px",
    "width": "179px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
